﻿using System;

using System.Collections.Generic;
using System.Text;
using Silvermoon.Controls.Classes;
using Silvermoon.Animations;
using System.Drawing;
using Silvermoon.Visuals;
using System.Diagnostics;
using Silvermoon.OpenGL;
using Silvermoon.OpenGL.Native;

namespace Silvermoon.Controls
{
    /// <summary>
    /// Control that has child controls while only one control is visible.
    /// Selector alows transitions to swap the active control.
    /// see also <seealso cref="T:PageContainer"/>.
    /// </summary>
    internal class PageTransition : Control
    {
        public const int EndLevel = 10000;
        public const int StartLevel = 0;

        const int maxAlpha = 255;
        #region fields

        private bool isAnimating;
        private ControlCollection controls;
        private Control current;
        private Control transitionControl;
        private PictureBox snapshot;

        private int level = EndLevel;
        private PageTransitionMode mode = PageTransitionMode.FadeDst;

        #endregion
        #region ctor

        public PageTransition()
            : base()
        {
            EnableSnapshot = true;
            controls = new ControlCollection(this);
        }


        #endregion
        #region props

        public PageTransitionMode Mode { get { return mode; } set { mode = value; } }

        /// <summary>
        /// Gets or sets whether to use a snapshot image of the current page when transitioning to another.
        /// Using a snapshot speeds up transition, but requires some extra texture memory.
        /// </summary>
        public bool EnableSnapshot { get; set; }

        public Control CurrentControl
        {
            get { return current; }
            set
            {
                if (current != value) { current = value; Invalidate(InvalidationFlag.LayoutAppearance | InvalidationFlag.Custom); }
            }
        }

        public bool IsAnimating { get { return isAnimating; } set { if (isAnimating != value) { isAnimating = value; OnAnimatingChanged(); } } }

        /// <summary>
        /// Gets or sets the control that is currently in transition with the current control
        /// </summary>
        public Control TransitionControl
        {
            get { return transitionControl; }
            set
            {
                if (transitionControl != value) { if (transitionControl != value) { OnTransitionControlChanged(value); Invalidate(InvalidationFlag.LayoutAppearance | InvalidationFlag.Custom); } }
            }
        }

        /// <summary>
        /// Gets or sets the transition level in a range between 0... 10000, where 0 is the start position and 10000 the final position.
        /// </summary>
        public int Level { get { return level; } set { value = ValidateLevel(value); if (value != level) { level = value; Invalidate(InvalidationFlag.LayoutAppearance | InvalidationFlag.Custom); } } }

        protected internal override IList<Control> SubControls { get { return controls; } }

        #endregion
        #region Methods

        private int ValidateLevel(int level)
        {
            if (level < 0) return 0;
            if (level > EndLevel) return EndLevel;
            return level;
        }

        protected override void OnLayout()
        {
            controls.Clear();
            if (transitionControl != null && IsAnimating)
            {
                if (snapshot != null) controls.Add(snapshot); else controls.Add(transitionControl);
            }

            if (current != null)
            {
                controls.Add(current);
                current.Bounds = GetSrcBounds();
                current.Alpha = GetSrcAlpha();
            }

            if (IsAnimating && transitionControl != null)
            {
                if (snapshot != null)
                {
                    snapshot.Bounds = GetDstBounds();
                    snapshot.Alpha = GetDstAlpha();
                }
                else
                {
                    transitionControl.Bounds = GetDstBounds();
                    transitionControl.Alpha = GetDstAlpha();
                }
            }

            base.OnLayout();
        }

        private int GetSrcAlpha()
        {
            if (!IsAnimating) return maxAlpha;
            if ((mode & PageTransitionMode.FadeDst) != 0)
            {
                int a = (level * maxAlpha / EndLevel);
                if ((mode & PageTransitionMode.Negative) != 0) a = maxAlpha - a;
                return a;
            }
            else return maxAlpha;
        }

        private int GetDstAlpha()
        {
            if (!IsAnimating) return maxAlpha;

            if ((mode & PageTransitionMode.FadeSrc) != 0)
            {
                int a = maxAlpha - (level * maxAlpha / EndLevel);
                if ((mode & PageTransitionMode.Negative) != 0) a = maxAlpha - a;
                return a;
            }
            else return maxAlpha;
        }

        private Rectangle GetSrcBounds()
        {
            if (!IsAnimating) return ClientBounds;

            int width = this.Width;
            int height = this.Height;
            int dx = level * width / EndLevel;
            int dy = level * height / EndLevel;

            int x = 0;
            int y = 0;
            if ((mode & PageTransitionMode.MoveDstToLeft) != 0) x = width - dx;
            else if ((mode & PageTransitionMode.MoveDstToRight) != 0) x = dx - width;

            if ((mode & PageTransitionMode.MoveDstUp) != 0) y = height - dy;
            else if ((mode & PageTransitionMode.MoveDstDown) != 0) y = dy - height;

            return new Rectangle(x, y, width, height);
        }

        private Rectangle GetDstBounds()
        {
            int width = this.Width;
            int height = this.Height;
            int dx = level * width / EndLevel;
            int dy = level * height / EndLevel;

            int x = 0;
            int y = 0;
            if ((mode & PageTransitionMode.MoveSrcToLeft) != 0) x = 0 - dx;
            else if ((mode & PageTransitionMode.MoveSrcToRight) != 0) x = dx;

            if ((mode & PageTransitionMode.MoveSrcUp) != 0) y = 0 - dy;
            else if ((mode & PageTransitionMode.MoveSrcDown) != 0) y = dy;

            return new Rectangle(x, y, width, height);
        }

        private static readonly Transformation[] snapshotTransform = new Transformation[] { new RotateTransform { Angle = 180, Axis = RotateAxis.XAxis } };

        /// <summary>
        /// Create a snapshot image of the current page and use it while transitioning to anothe page.
        /// Using a snapshot speeds up transition since there is only on image to render instead of each control in the previous page.
        /// but as overhead, it requires extra texture memory.
        /// </summary>
        /// <param name="value"></param>
        protected virtual void OnTransitionControlChanged(Control value)
        {
            if (value != null)
            {
                if (snapshot != null)
                {
                    snapshot.Image.Dispose();
                    snapshot.Dispose();
                    snapshot = null;
                }
                if (EnableSnapshot)
                {
                    Texture texture = Capture();
                    if (texture!=null)
                    {
                        snapshot = new PictureBox();
                        snapshot.Transformations = snapshotTransform;
                        snapshot.Image = texture;
                    }
                }
            }
            else
            {
                if (snapshot != null)
                {
                    snapshot.Image.Dispose();
                    snapshot.Dispose();
                    snapshot = null;
                }
            }
            transitionControl = value;
        }

        /// <summary>
        /// When the transition is completed, release the snapshot texture, if it exists:
        /// </summary>
        protected void OnAnimatingChanged()
        {
            if (!isAnimating) TransitionControl = null;
        }


        #endregion
    }
}